#!/usr/bin/env python3
"""
API 连通性测试脚本
测试所有外部服务 API 是否可用

测试项目：
1. 和风天气 API (QWeather)
2. 腾讯地图 API (POI搜索、路线规划)
3. OpenAI API (文本生成)
4. OpenAI TTS API (语音合成)
5. Suno AI API (音乐生成)

使用方法：
    cd backend
    python -m tests.test_api_connectivity

或者：
    python tests/test_api_connectivity.py
"""

import os
import sys
import json
import time
import requests
from datetime import datetime
from typing import Dict, Any, Tuple
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

# 颜色输出
class Colors:
    GREEN = '\033[92m'
    RED = '\033[91m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    RESET = '\033[0m'
    BOLD = '\033[1m'


def print_header(title: str):
    """打印标题"""
    print(f"\n{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.RESET}")
    print(f"{Colors.BOLD}{Colors.BLUE}{title:^60}{Colors.RESET}")
    print(f"{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.RESET}\n")


def print_result(name: str, success: bool, message: str, details: str = None):
    """打印测试结果"""
    status = f"{Colors.GREEN}✓ 通过{Colors.RESET}" if success else f"{Colors.RED}✗ 失败{Colors.RESET}"
    print(f"  {status} {name}")
    if message:
        print(f"       {Colors.YELLOW}{message}{Colors.RESET}")
    if details:
        print(f"       {details}")


def test_qweather_api() -> Tuple[bool, str, Dict]:
    """测试和风天气 API"""
    api_key = os.getenv('QWEATHER_KEY', '')
    
    if not api_key or api_key == 'YOUR_QWEATHER_KEY':
        return False, "未配置 QWEATHER_KEY", {}
    
    # 测试位置：北京天安门
    test_location = "116.397128,39.903738"
    # 商业版使用 api.qweather.com，免费版使用 devapi.qweather.com
    qweather_host = os.getenv('QWEATHER_HOST', 'api.qweather.com')
    
    try:
        # 测试实时天气
        url = f"https://{qweather_host}/v7/weather/now"
        params = {
            'key': api_key,
            'location': test_location,
            'lang': 'zh'
        }
        
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        
        if result.get('code') == '200':
            now = result.get('now', {})
            details = {
                'temperature': now.get('temp'),
                'condition': now.get('text'),
                'humidity': now.get('humidity'),
                'wind_speed': now.get('windSpeed')
            }
            return True, f"温度: {now.get('temp')}°C, 天气: {now.get('text')}", details
        else:
            # 检查是否是 Host 绑定问题
            error_detail = result.get('error', {}).get('detail', '')
            if 'Invalid Host' in str(error_detail) or response.status_code == 403:
                return False, "API Key 绑定了域名限制，需要在和风天气控制台创建'通用API'类型的Key (当前使用mock数据降级)", result
            error_msg = error_detail or result.get('code', 'Unknown')
            return False, f"API返回错误: {error_msg}", result
            
    except requests.Timeout:
        return False, "请求超时", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_qweather_air_api() -> Tuple[bool, str, Dict]:
    """测试和风天气空气质量 API"""
    api_key = os.getenv('QWEATHER_KEY', '')
    
    if not api_key or api_key == 'YOUR_QWEATHER_KEY':
        return False, "未配置 QWEATHER_KEY", {}
    
    test_location = "116.397128,39.903738"
    qweather_host = os.getenv('QWEATHER_HOST', 'api.qweather.com')
    
    try:
        url = f"https://{qweather_host}/v7/air/now"
        params = {
            'key': api_key,
            'location': test_location,
            'lang': 'zh'
        }
        
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        
        if result.get('code') == '200':
            now = result.get('now', {})
            return True, f"AQI: {now.get('aqi')}, 等级: {now.get('category')}", now
        else:
            error_detail = result.get('error', {}).get('detail', '')
            if 'Invalid Host' in str(error_detail) or response.status_code == 403:
                return False, "API Key 绑定了域名限制 (当前使用mock数据降级)", result
            error_msg = error_detail or result.get('code', 'Unknown')
            return False, f"API返回错误: {error_msg}", result
            
    except requests.Timeout:
        return False, "请求超时", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_tencent_map_poi_api() -> Tuple[bool, str, Dict]:
    """测试腾讯地图 POI 搜索 API"""
    api_key = os.getenv('TENCENT_MAP_KEY', '')
    
    if not api_key or api_key == 'YOUR_TENCENT_MAP_KEY':
        return False, "未配置 TENCENT_MAP_KEY", {}
    
    try:
        url = "https://apis.map.qq.com/ws/place/v1/search"
        params = {
            'key': api_key,
            'boundary': 'nearby(39.903738,116.397128,3000)',
            'keyword': '公园',
            'page_size': 5,
            'page_index': 1
        }
        
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        
        if result.get('status') == 0:
            data = result.get('data', [])
            count = len(data)
            if count > 0:
                first_poi = data[0]
                return True, f"找到 {count} 个POI, 第一个: {first_poi.get('title')}", {'count': count, 'first': first_poi}
            else:
                return True, "搜索成功但无结果", {'count': 0}
        else:
            return False, f"API返回错误: {result.get('message')}", result
            
    except requests.Timeout:
        return False, "请求超时", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_tencent_map_direction_api() -> Tuple[bool, str, Dict]:
    """测试腾讯地图步行路线规划 API"""
    api_key = os.getenv('TENCENT_MAP_KEY', '')
    
    if not api_key or api_key == 'YOUR_TENCENT_MAP_KEY':
        return False, "未配置 TENCENT_MAP_KEY", {}
    
    try:
        url = "https://apis.map.qq.com/ws/direction/v1/walking"
        params = {
            'key': api_key,
            'from': '39.903738,116.397128',  # 天安门
            'to': '39.915119,116.403963'      # 故宫
        }
        
        response = requests.get(url, params=params, timeout=10)
        result = response.json()
        
        if result.get('status') == 0:
            route_result = result.get('result', {})
            routes = route_result.get('routes', [])
            if routes:
                route = routes[0]
                distance = route.get('distance', 0)
                duration = route.get('duration', 0)
                return True, f"距离: {distance}米, 预计时间: {duration//60}分钟", route
            else:
                return False, "未找到路线", result
        else:
            return False, f"API返回错误: {result.get('message')}", result
            
    except requests.Timeout:
        return False, "请求超时", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_openai_chat_api() -> Tuple[bool, str, Dict]:
    """测试 OpenAI Chat API (文本生成)"""
    api_key = os.getenv('OPENAI_API_KEY', '')
    api_base = os.getenv('OPENAI_API_BASE', 'https://api.openai.com/v1')
    
    if not api_key:
        return False, "未配置 OPENAI_API_KEY", {}
    
    try:
        url = f"{api_base}/chat/completions"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
        data = {
            'model': 'gpt-4o-mini',
            'messages': [
                {'role': 'user', 'content': '请用一句话描述北京的天气特点'}
            ],
            'max_tokens': 100
        }
        
        response = requests.post(url, headers=headers, json=data, timeout=30)
        result = response.json()
        
        if response.status_code == 200:
            choices = result.get('choices', [])
            if choices:
                content = choices[0].get('message', {}).get('content', '')
                return True, f"回复: {content[:50]}...", {'content': content}
            else:
                return False, "无返回内容", result
        else:
            error = result.get('error', {})
            return False, f"API错误: {error.get('message', response.status_code)}", result
            
    except requests.Timeout:
        return False, "请求超时 (30秒)", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_openai_tts_api() -> Tuple[bool, str, Dict]:
    """测试 OpenAI TTS API (语音合成)"""
    api_key = os.getenv('OPENAI_API_KEY', '')
    api_base = os.getenv('OPENAI_API_BASE', 'https://api.openai.com/v1')
    
    if not api_key:
        return False, "未配置 OPENAI_API_KEY", {}
    
    try:
        url = f"{api_base}/audio/speech"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
        data = {
            'model': 'tts-1',
            'input': '欢迎使用步语散步应用',
            'voice': 'alloy',
            'response_format': 'mp3'
        }
        
        response = requests.post(url, headers=headers, json=data, timeout=30)
        
        if response.status_code == 200:
            # 检查返回的是否是音频数据
            content_type = response.headers.get('Content-Type', '')
            audio_size = len(response.content)
            
            if audio_size > 0:
                return True, f"生成音频大小: {audio_size} bytes", {'size': audio_size, 'content_type': content_type}
            else:
                return False, "返回音频为空", {}
        else:
            try:
                result = response.json()
                error = result.get('error', {})
                return False, f"API错误: {error.get('message', response.status_code)}", result
            except:
                return False, f"HTTP错误: {response.status_code}", {}
            
    except requests.Timeout:
        return False, "请求超时 (30秒)", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def test_suno_api() -> Tuple[bool, str, Dict]:
    """测试 Suno AI API (音乐生成)
    
    注意: Suno API 可能需要特定的端点和认证方式
    这里提供一个基础测试框架，需要根据实际 API 文档调整
    """
    api_key = os.getenv('SUNO_API_KEY', '')
    
    if not api_key:
        return False, "未配置 SUNO_API_KEY", {}
    
    # Suno API 端点 (需要根据实际情况调整)
    # 常见的 Suno API 代理服务端点
    suno_api_base = os.getenv('SUNO_API_BASE', 'https://api.sunoapi.com')
    
    try:
        # 尝试获取账户信息或配额信息来验证 API 可用性
        url = f"{suno_api_base}/api/get_limit"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
        
        response = requests.get(url, headers=headers, timeout=15)
        
        if response.status_code == 200:
            result = response.json()
            return True, f"API可用, 响应: {str(result)[:50]}", result
        elif response.status_code == 401:
            return False, "API Key 无效或未授权", {}
        elif response.status_code == 404:
            # 尝试另一个常见端点
            return False, "API端点不存在，请检查 SUNO_API_BASE 配置", {}
        else:
            try:
                result = response.json()
                return False, f"API错误: {result}", result
            except:
                return False, f"HTTP错误: {response.status_code}", {}
            
    except requests.Timeout:
        return False, "请求超时 (15秒)", {}
    except requests.ConnectionError:
        return False, "无法连接到 Suno API 服务器", {}
    except Exception as e:
        return False, f"请求异常: {str(e)}", {}


def run_all_tests():
    """运行所有 API 连通性测试"""
    print_header("步语 (BuYu) API 连通性测试")
    print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
    
    results = {
        'total': 0,
        'passed': 0,
        'failed': 0,
        'details': {}
    }
    
    # 测试列表
    tests = [
        ("和风天气 - 实时天气", test_qweather_api),
        ("和风天气 - 空气质量", test_qweather_air_api),
        ("腾讯地图 - POI搜索", test_tencent_map_poi_api),
        ("腾讯地图 - 步行路线", test_tencent_map_direction_api),
        ("OpenAI - 文本生成", test_openai_chat_api),
        ("OpenAI - TTS语音合成", test_openai_tts_api),
        ("Suno AI - 音乐生成", test_suno_api),
    ]
    
    # 分组显示
    groups = {
        "天气服务 (和风天气)": ["和风天气 - 实时天气", "和风天气 - 空气质量"],
        "地图服务 (腾讯地图)": ["腾讯地图 - POI搜索", "腾讯地图 - 步行路线"],
        "AI服务 (OpenAI)": ["OpenAI - 文本生成", "OpenAI - TTS语音合成"],
        "音乐服务 (Suno AI)": ["Suno AI - 音乐生成"]
    }
    
    test_dict = {name: func for name, func in tests}
    
    for group_name, test_names in groups.items():
        print(f"{Colors.BOLD}▶ {group_name}{Colors.RESET}")
        
        for test_name in test_names:
            if test_name in test_dict:
                results['total'] += 1
                
                try:
                    success, message, details = test_dict[test_name]()
                    
                    if success:
                        results['passed'] += 1
                    else:
                        results['failed'] += 1
                    
                    results['details'][test_name] = {
                        'success': success,
                        'message': message,
                        'details': details
                    }
                    
                    print_result(test_name, success, message)
                    
                except Exception as e:
                    results['failed'] += 1
                    results['details'][test_name] = {
                        'success': False,
                        'message': f"测试执行异常: {str(e)}",
                        'details': {}
                    }
                    print_result(test_name, False, f"测试执行异常: {str(e)}")
        
        print()  # 分组间空行
    
    # 打印汇总
    print_header("测试汇总")
    print(f"  总计: {results['total']} 项测试")
    print(f"  {Colors.GREEN}通过: {results['passed']} 项{Colors.RESET}")
    print(f"  {Colors.RED}失败: {results['failed']} 项{Colors.RESET}")
    
    if results['failed'] > 0:
        print(f"\n{Colors.YELLOW}⚠ 部分服务不可用，请检查以下配置:{Colors.RESET}")
        for name, detail in results['details'].items():
            if not detail['success']:
                print(f"  - {name}: {detail['message']}")
    
    print(f"\n{Colors.BLUE}配置文件位置: backend/.env{Colors.RESET}")
    print(f"{Colors.BLUE}配置示例: backend/.env.example{Colors.RESET}\n")
    
    return results


def save_test_report(results: Dict[str, Any], filename: str = None):
    """保存测试报告到文件"""
    if filename is None:
        filename = f"api_test_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    
    report = {
        'test_time': datetime.now().isoformat(),
        'summary': {
            'total': results['total'],
            'passed': results['passed'],
            'failed': results['failed'],
            'pass_rate': f"{results['passed']/results['total']*100:.1f}%" if results['total'] > 0 else "0%"
        },
        'details': results['details']
    }
    
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(report, f, ensure_ascii=False, indent=2)
    
    print(f"测试报告已保存到: {filename}")
    return filename


if __name__ == '__main__':
    # 检查是否在正确的目录
    if not os.path.exists('.env') and os.path.exists('backend/.env'):
        os.chdir('backend')
        load_dotenv()
    
    # 运行测试
    results = run_all_tests()
    
    # 可选：保存报告
    if '--save' in sys.argv or '-s' in sys.argv:
        save_test_report(results)
    
    # 返回退出码
    sys.exit(0 if results['failed'] == 0 else 1)
